#pragma once

#include <vex/core/handle_policy.h>
#include <vex/core/as_any.h>
#include <vex/core/as_intrusive.h>
#include <vex/core/contract/memory_resource.h>
#include <vex/core/implementation/allocator_policy.h>
#include <cstddef>

namespace vex { 
    namespace core {
        template<class T>
        struct any_allocator_traits {
            // required by handle_policy:
            typedef contract::memory_resource contract_type;
            // required by handle_policy:
            typedef boost::intrusive_ptr<contract_type> holder_type;
            // required by all policies
            typedef contract_type* item_type;

            // required by std::allocator policy
            typedef T                 value_type;
            typedef value_type*       pointer;
            typedef value_type const* const_pointer;
            typedef value_type&       reference;
            typedef value_type const& const_reference;
            typedef std::size_t       size_type;
            typedef std::ptrdiff_t    difference_type;
        };

        template<class T>
        class any_allocator : public core::handle_policy<
            any_allocator<T>,
            any_allocator_traits<T>,
            implementation::allocator_policy< any_allocator<T>, any_allocator_traits<T> >
        >
        {
        public:
            /// <summary>
            /// Big five implementation and common public API of <c>handle_policy</c>
            /// </summary>
            VEX_ANY_HANDLE_DEFAULT_BODY_IMPL(
                any_allocator, 
                any_allocator_traits<T>,
                VEX_TRAITS_IS_DEPENDENT_NAME);

            // allocator concept specific requirements: 
            template <class U> struct rebind { typedef any_allocator<U> other; };

            template <class U> any_allocator(any_allocator<U> const &p_other)
                : m_holder(p_other.get())
            {
            }

            // gee, this is too strict....
            template <class U> friend bool operator==(
                const any_allocator& p_lhs, const any_allocator<U>& p_rhs)
            {
                return p_lhs.get() == p_rhs.get();
            }

            template <class U> friend bool operator!=(
                const any_allocator& p_lhs, const any_allocator<U>& p_rhs)
            {
                return p_lhs.get() != p_rhs.get();
            }
        private:
            holder_type m_holder;
        };

        /// adl support, must be in the same namespace as contract
        namespace contract {
            template<class T> auto adl_as_any_from_out(memory_resource* p_handle)
                -> any_allocator<T>
            {
                return any_allocator<T>(p_handle, false);
            }

            template<class T> auto adl_as_any_from_in(memory_resource* p_handle)
                -> any_allocator<T>
            {
                return any_allocator<T>(p_handle, true);
            }
        }
    }

    using core::any_allocator;
}